# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
# X-SPDX-Copyright-Text: (c) Copyright 2022 Xilinx, Inc.

# Override this to run only the subset of the tests beginning with the filter
# e.g. tests in a single directory: UNIT_TEST_FILTER=lib/transport/ip/
#      single test: UNIT_TEST_FILTER=lib/transport/ip/tcp_rx
UNIT_TEST_FILTER ?=

# Override this to run tests under a wrapper (e.g. gdb, valgrind)
UNIT_TEST_WRAPPER ?=

# All the tests that can be run. Can be filtered using UNIT_TEST_FILTER.
# In principle, this could be autogenerated by searching the source directory.
#
# Tests under `lib` are for code defined in library objects and will be linked
# with the corresponding object built from the top-level `src/lib` tree, so
# the path name must match that of the object under test.
#
# Tests under `header` (or any other top-level directory apart from `lib`) are
# for code defined in headers, and are not linked with any library objects.
# There are no restrictions on the path name, but it is helpful if it matches
# the header under test.
ALL_UNIT_TESTS := \
  header/ci/internal/ip_timestamp \
  header/transport/unix/ul_epoll \
  lib/transport/ip/netif_init \
  lib/transport/ip/tcp_rx \
  lib/ciul/checksum \
  lib/ciul/efct_ubufs \
  lib/ciul/efct_vi \
  lib/ciul/shrub_connection \
  lib/ciul/shrub_queue \
  lib/ciul/shrub_server \

# The tests to be run, and their corresponding files
TESTS := $(filter $(UNIT_TEST_FILTER)%, $(ALL_UNIT_TESTS))
TARGETS := $(TESTS:%=$(AppPattern))
OBJECTS := $(TESTS:%=%.o)
PASSED := $(TESTS:%=%.passed)

# Library object names are mangled with a prefix. Deal with that madness here.
LIB_PREFIXES := lib/transport/common/ci_tp_common_ \
		lib/transport/ip/ci_ip_ \
		lib/transport/unix/ci_unix_ \
		lib/ciapp/ci_app_ \
		lib/citools/ci_tools_ \
		lib/ciul/ci_ul_ \

lib_prefix = $(notdir $(filter $(dir $(1))%,$(LIB_PREFIXES)))
lib_object = ../../$(dir $(1))$(call lib_prefix,$(1))$(notdir $(1)).o

# TODO can we rely on a sufficiently up-to-date version of make?
.SECONDEXPANSION:

all: $(PASSED)

# Sentinel files indicate that a test has passed. The test only needs to be
# run again if the sentinel is out of date.
$(PASSED): %.passed: %
	@echo UNIT TEST $<
	@$(UNIT_TEST_WRAPPER) $< && touch $@

# Object files require their corresponding directory. Depend on a sentinel file
# rather than the directory, whose timestamp may change when files are modified.
$(OBJECTS): % : $$(@D)/.unit_test_dir
%/.unit_test_dir:
	@mkdir -p $(@D)
	@touch $@

# Attempting to ignore unresolved symbols at link time will fail if building
# a position-independent executable (PIE). Different compiler versions have
# different requirements to specify this:
# - if there is a -no-pie option, we must use that as the default may be PIE
# - otherwise, we don't specify PIE, and hope that the default is non-PIE
NO_PIE = $(shell echo 'int main(void){return 0;}' | \
                 $(CC) -x c -no-pie - -o /dev/null 2>/dev/null && echo -no-pie)

# Allow tests to include headers in the same directory as the code under test,
# and relative to the top-level `src/lib` directory.
$(filter lib/%, $(TARGETS)): MMAKE_DIR_CFLAGS += -I$(TOPPATH)/src/$(dir $@)
$(TARGETS): MMAKE_DIR_CFLAGS += -I$(TOPPATH)/src/lib

# Test programs are linked with the object under test, and stub dependencies.
#
# CAVEAT: the fragmented build system means that the object under test will NOT
# be rebuilt if out of date. A top-level build is needed to make sure it's up
# to date before building the tests. This sadly means we can't reliably run an
# invididual test without waiting for several seconds of flappery first.
$(TARGETS): MMAKE_DIR_LINKFLAGS += -Wl,--unresolved-symbols=ignore-all $(NO_PIE)
$(TARGETS): MMAKE_LIBS += -ldl
$(filter lib/%, $(TARGETS)): $$(call lib_object,$$@)
$(TARGETS): %: %.o stubs.o
	(libs=$(MMAKE_LIBS); $(MMakeLinkCApp))

# We want to use ef_vi_transmit_unbundle in this test, but it's hidden away
# elsewhere, so lets also link against that object.
lib/ciul/efct_vi: ../../lib/ciul/pt_tx.o

# The build system relies on a convoluted web of makefiles in subdirectories
# of both source and build trees to generate the dependencies. Lets do it the
# easy way instead. TODO remove this once the build system is more sensible.
$(OBJECTS): MMAKE_DIR_CFLAGS += -MMD -MP
-include $(subst .o,.d,$(OBJECTS))
